A comprehensive guide to CSS @scroll-timeline, covering its syntax, properties, usage, and advanced animation techniques. Learn how to create engaging scroll-driven animations for modern web experiences.
Mastering CSS @scroll-timeline: Animation Control Through Scroll Progress
In the ever-evolving landscape of web development, creating engaging and interactive user experiences is paramount. CSS offers various tools for achieving this, and one of the most powerful, yet often overlooked, is the @scroll-timeline at-rule. This feature allows developers to tie animations directly to the scroll progress of an element, creating captivating scroll-driven animations. This article provides a comprehensive exploration of @scroll-timeline, covering its syntax, properties, practical usage, and advanced animation techniques to elevate your web designs.
What is CSS @scroll-timeline?
@scroll-timeline is a CSS at-rule that defines a scroll timeline, which is essentially a sequence of states that correspond to the scroll position of a specified element. Instead of relying on traditional time-based animations, @scroll-timeline links animation progress to the user's scrolling action. This results in a more natural and responsive animation, as the animation speed is directly controlled by the user's scrolling behavior.
This opens up exciting possibilities for:
- Visual storytelling: Reveal content progressively as the user scrolls.
- Interactive data visualization: Animate charts and graphs as the user explores data.
- Parallax effects: Create depth and dimension by animating different elements at varying speeds based on scroll position.
- Progress indicators: Visually represent the user's progress through a long document.
Syntax and Properties
The basic syntax of the @scroll-timeline at-rule is as follows:
@scroll-timeline timeline-name {
source: auto | <element-selector>;
orientation: auto | block | inline;
scroll-offsets: <scroll-offset>[ , <scroll-offset> ]*;
}
Let's break down each property:
timeline-name
This is a unique identifier for your scroll timeline. You'll use this name to reference the timeline when applying it to an animation.
Example:
@scroll-timeline my-scroll-timeline {
/* ... */
}
source
This property specifies the element whose scroll position will drive the animation. It can have two values:
auto: The browser automatically determines the scrolling element. This is often the document's viewport (the browser window).<element-selector>: A CSS selector that identifies the specific element to use as the scroll source. This allows you to target specific containers or sections on your page.
Example (using the viewport as the source):
@scroll-timeline my-scroll-timeline {
source: auto; /* Uses the viewport */
/* ... */
}
Example (using a specific element as the source):
@scroll-timeline my-scroll-timeline {
source: #scrollable-container; /* Uses the element with ID "scrollable-container" */
/* ... */
}
orientation
This property specifies the scroll direction to use for the timeline. It determines whether the animation is driven by vertical or horizontal scrolling. It can have three values:
auto: The browser automatically determines the scroll direction based on the dominant scroll direction of the source element.block: Uses the block (vertical, in most writing modes) scroll direction.inline: Uses the inline (horizontal, in most writing modes) scroll direction.
Example (using vertical scrolling):
@scroll-timeline my-scroll-timeline {
source: auto;
orientation: block; /* Vertical scrolling */
/* ... */
}
Example (using horizontal scrolling):
@scroll-timeline my-scroll-timeline {
source: #horizontal-scroll-container;
orientation: inline; /* Horizontal scrolling */
/* ... */
}
scroll-offsets
This property defines the scroll positions that correspond to specific points in the animation. It's an optional property, and if not specified, the animation will play from the start to the end of the scrollable area. When used, you can define one or more scroll offsets, each specifying a scroll position and a corresponding point in the animation's progress.
The syntax for a <scroll-offset> is:
<scroll-offset> = <length-percentage> [ at <length-percentage> ]
Where:
- The first
<length-percentage>represents the scroll position within the scrollable area. - The optional
at <length-percentage>represents the corresponding animation progress (0% to 100%). If omitted, the animation progress is evenly distributed between the defined scroll offsets.
Examples:
/* Scroll position 200px corresponds to animation progress 0% */
scroll-offsets: 200px at 0%;
/* Scroll position at 50% of the scrollable area corresponds to animation progress 50% */
scroll-offsets: 50% at 50%;
/* Multiple offsets: */
scroll-offsets: 0px at 0%, 500px at 50%, 1000px at 100%;
/* Without the "at" keyword - evenly distributed animation progress: */
scroll-offsets: 0px, 500px, 1000px; /* Equivalent to 0px at 0%, 500px at 50%, 1000px at 100% */
Important Considerations for scroll-offsets:
- If you specify
scroll-offsets, ensure that they cover the range of the scrollable area to avoid unexpected animation behavior. - The animation progress is interpolated between the defined scroll offsets.
- If you don't specify
scroll-offsets, the animation progress will be evenly distributed across the entire scrollable area.
Applying the Scroll Timeline to an Animation
Once you've defined your scroll timeline, you need to apply it to a CSS animation using the animation-timeline property.
The syntax is simple:
animation-timeline: timeline-name; /* Use the name you defined in @scroll-timeline */
You'll also need to define a standard CSS animation using @keyframes. The animation defines the changes in CSS properties that will occur as the user scrolls. Furthermore, you'll want to ensure the `animation-range` CSS property is set. It defines the range of the scroll timeline which will activate the animation.
Here's a complete example:
/* Define the scroll timeline */
@scroll-timeline my-scroll-timeline {
source: auto;
orientation: block;
}
/* Define the animation */
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Apply the animation and scroll timeline to an element */
.animated-element {
animation: fade-in 1s forwards;
animation-timeline: my-scroll-timeline;
animation-range: entry 25% cover 75%; /* entry and cover are keyword values for the start and end ranges */
}
In this example:
- The
@scroll-timelinenamedmy-scroll-timelineis defined, using the viewport as the source and vertical scrolling as the orientation. - The
@keyframesnamedfade-indefines a simple fade-in and slide-up animation. - The
.animated-elementclass has thefade-inanimation applied, but instead of being triggered by a timer, it's controlled by themy-scroll-timeline. - The animation-range defines that the animation should start when the top edge of element enters the bottom 25% of the viewport and finish when it leaves the top 25%.
Practical Examples and Use Cases
Let's explore some practical examples of how you can use @scroll-timeline to create engaging web experiences.
1. Progressively Revealing Content
This is a common use case where you reveal content as the user scrolls down the page. Imagine a long-form article with sections that fade in as they come into view.
HTML:
<section class="content-section">
<h2>Section 1</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
</section>
<section class="content-section">
<h2>Section 2</h2>
<p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...</p>
</section>
CSS:
@scroll-timeline reveal-timeline {
source: auto;
orientation: block;
}
@keyframes reveal {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.content-section {
animation: reveal 1s forwards;
animation-timeline: reveal-timeline;
animation-range: entry 25% cover 75%;
}
In this example, each .content-section will fade in as it scrolls into the viewport. The `animation-range` ensures the animation starts when the section's top edge enters the bottom 25% of the viewport and finishes when the section leaves the top 25%.
2. Parallax Effects
Parallax effects create a sense of depth by moving background elements at different speeds than foreground elements. @scroll-timeline makes it easy to implement parallax scrolling.
HTML:
<div class="parallax-container">
<div class="background-element"></div>
<div class="foreground-element">
<h2>Parallax Section</h2>
<p>Some content here...</p>
</div>
</div>
CSS:
.parallax-container {
position: relative;
height: 500px; /* Adjust as needed */
overflow: hidden;
}
.background-element {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('background.jpg'); /* Replace with your image */
background-size: cover;
transform: translateZ(-1px) scale(2); /* Creates the parallax effect */
transform-origin: top;
pointer-events: none; /* Allows clicking on foreground elements */
animation: parallax-bg 1s linear forwards;
animation-timeline: parallax-timeline;
animation-range: 0vh 100vh;
}
.foreground-element {
position: relative;
z-index: 1;
padding: 50px;
background-color: rgba(255, 255, 255, 0.8);
}
@scroll-timeline parallax-timeline {
source: auto;
orientation: block;
}
@keyframes parallax-bg {
0% { transform: translateZ(-1px) scale(2) translateY(0px); }
100% { transform: translateZ(-1px) scale(2) translateY(-50vh); }
}
In this example, the .background-element is positioned behind the .foreground-element and scaled up using transform. The `parallax-bg` animation is then applied, causing the background to move slower than the foreground as the user scrolls, creating the parallax effect. The `animation-range` ensures the animation runs throughout the entire height of the viewport (0vh to 100vh).
3. Animating a Progress Bar
You can use @scroll-timeline to create a progress bar that visually represents the user's scroll progress through a document.
HTML:
<div class="progress-bar-container">
<div class="progress-bar"></div>
</div>
<div class="content">
<!-- Your content here -->
</div>
CSS:
.progress-bar-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 5px;
background-color: #eee;
z-index: 1000;
}
.progress-bar {
height: 100%;
width: 0%;
background-color: #007bff;
animation: progress-bar-fill 1s linear forwards;
animation-timeline: document-scroll-timeline;
}
@scroll-timeline document-scroll-timeline {
source: auto;
orientation: block;
}
@keyframes progress-bar-fill {
0% {
width: 0%;
}
100% {
width: 100%;
}
}
In this example, the .progress-bar's width is animated from 0% to 100% as the user scrolls through the document. The @scroll-timeline is named `document-scroll-timeline`, making it clear what it represents.
Advanced Techniques
Once you have a grasp of the basics, you can explore some advanced techniques to create even more sophisticated scroll-driven animations.
1. Using scroll-offsets for Precise Control
The scroll-offsets property allows you to map specific scroll positions to specific animation progress values. This is useful when you want to trigger certain animation states at precise points during scrolling.
@scroll-timeline custom-timeline {
source: #scrollable-container;
orientation: block;
scroll-offsets: 100px at 0%, 500px at 50%, 1000px at 100%;
}
@keyframes custom-animation {
0% {
transform: translateX(-100px);
opacity: 0;
}
50% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(100px);
opacity: 0;
}
}
.animated-element {
animation: custom-animation 1s forwards;
animation-timeline: custom-timeline;
}
In this example, the animation will:
- Start at
translateX(-100px)andopacity: 0when the scroll position is 100px. - Reach
translateX(0)andopacity: 1when the scroll position is 500px. - End at
translateX(100px)andopacity: 0when the scroll position is 1000px.
2. Combining with JavaScript
While @scroll-timeline offers powerful animation control through CSS, you can combine it with JavaScript for even greater flexibility. For example, you might use JavaScript to:
- Dynamically calculate and update
scroll-offsetsbased on viewport size or content changes. - Trigger additional JavaScript-based effects or interactions based on scroll progress.
- Implement fallback solutions for browsers that don't fully support
@scroll-timeline.
Here's a basic example of using JavaScript to read the scroll progress and update a CSS variable:
const scrollableElement = document.getElementById('scrollable-container');
const animatedElement = document.querySelector('.animated-element');
scrollableElement.addEventListener('scroll', () => {
const scrollPosition = scrollableElement.scrollTop;
const maxScroll = scrollableElement.scrollHeight - scrollableElement.clientHeight;
const scrollPercentage = (scrollPosition / maxScroll) * 100;
animatedElement.style.setProperty('--scroll-progress', scrollPercentage + '%');
});
You can then use this CSS variable within your animation:
@keyframes custom-animation {
0% {
transform: translateX(calc(var(--scroll-progress) * -1px));
}
100% {
transform: translateX(calc(var(--scroll-progress) * 1px));
}
}
.animated-element {
--scroll-progress: 0%; /* Initial value */
animation: custom-animation 1s linear forwards;
animation-timeline: scroll-driven-timeline;
}
3. Leveraging Different Easing Functions
While animation-timing-function isn't directly applicable to the scroll timeline itself (as the timeline is driven by scroll progress, not time), you can still use easing functions within your @keyframes to control the animation's speed and rhythm at different stages. Experiment with different easing functions like ease-in, ease-out, ease-in-out, or even custom cubic bezier curves to achieve the desired effect.
Browser Compatibility and Fallbacks
As of late 2023, @scroll-timeline enjoys relatively good browser support in modern browsers like Chrome, Edge, Firefox, and Safari. However, it's essential to check the current compatibility status on websites like Can I use... before implementing it in production.
For browsers that don't support @scroll-timeline, you can provide a fallback using traditional JavaScript-based scroll event listeners and animation libraries like GSAP (GreenSock Animation Platform) or Anime.js. You can also use CSS feature queries (@supports) to conditionally apply either the @scroll-timeline-based animations or the JavaScript-based fallbacks.
@supports (animation-timeline: scroll()) {
/* Apply @scroll-timeline-based animations */
.animated-element {
animation: fade-in 1s forwards;
animation-timeline: my-scroll-timeline;
}
} @else {
/* Apply JavaScript-based fallback */
.animated-element {
/* Hide initially */
opacity: 0;
}
/* (JavaScript code to detect scroll and apply opacity) */
}
Accessibility Considerations
When using @scroll-timeline or any animation technique, it's crucial to consider accessibility. Ensure that your animations don't cause:
- Seizures: Avoid flashing or rapidly changing animations.
- Distraction: Provide a way for users to pause or disable animations, especially if they are lengthy or distracting.
- Cognitive overload: Use animations sparingly and ensure they serve a clear purpose, rather than being purely decorative.
- Motion sickness: Be mindful of parallax effects, as they can trigger motion sickness in some users.
Consider providing alternative ways to access the information presented through animations, such as static content or descriptive text. Use ARIA attributes to provide semantic meaning and context to assistive technologies.
Best Practices
Here are some best practices to keep in mind when working with @scroll-timeline:
- Use descriptive timeline names: Choose timeline names that clearly indicate their purpose (e.g.,
parallax-background-timeline,reveal-section-timeline). - Keep animations performant: Optimize your animations to avoid performance bottlenecks. Use hardware-accelerated CSS properties like
transformandopacitywhenever possible. - Test thoroughly: Test your animations on different devices and browsers to ensure they work as expected and don't cause any accessibility or performance issues.
- Start Simple: Begin with simple animations and gradually add complexity as you gain experience.
- Consider the User Experience: Ensure your animations enhance the user experience, not detract from it. Avoid overly complex or distracting animations.
- Use the `animation-range` CSS property: Ensure that animations only trigger when an element is in the viewport for a smooth and predictable experience.
Conclusion
@scroll-timeline is a powerful CSS feature that enables developers to create engaging and interactive scroll-driven animations. By linking animations to the user's scrolling behavior, you can create more natural and responsive web experiences. By understanding its syntax, properties, and advanced techniques, you can leverage @scroll-timeline to elevate your web designs and create captivating user journeys. Remember to consider browser compatibility, accessibility, and performance when implementing @scroll-timeline, and always prioritize the user experience.